package com.fly.core.aop;

import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;

import com.fly.core.JsonResult;
import com.fly.core.annotation.Item;
import com.fly.core.annotation.OpenApi;
import com.fly.core.utils.HttpServletUtils;
import com.fly.core.utils.jackson.JsonBeanUtils;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Aspect
@Component
public class OpenApiAspect
{
    /**
     * 是否启用检查
     */
    @Value("${openapi.check.enable:true}")
    private boolean checkEnable;
    
    /**
     * 使用OpenApi注解标注的类
     */
    @Around("@annotation(openApi)")
    public Object around(ProceedingJoinPoint joinPoint, OpenApi openApi)
        throws Throwable
    {
        if (checkEnable)
        {
            List<String> errors = new ArrayList<>();
            HttpServletRequest request = HttpServletUtils.getHttpServletRequest();
            if (request == null)
            {
                log.error("OpenApi使用错误，当前请求非web请求");
                errors.add("OpenApi使用错误，当前请求非web请求");
            }
            else
            {
                List<Item> items = Arrays.asList(openApi.checkItems());
                if (items.contains(Item.ALL) || items.contains(Item.TOKEN))
                {
                    log.info("需要检查鉴权");
                    String token = request.getHeader("token");
                    if (StringUtils.isBlank(token))
                    {
                        errors.add("检查鉴权失败");
                    }
                }
                if (items.contains(Item.ALL) || items.contains(Item.NO_REPEAT))
                {
                    log.info("需要防重放");
                    String timestamp = request.getHeader("timestamp");
                    if (StringUtils.isBlank(timestamp))
                    {
                        errors.add("检查防重放失败");
                    }
                }
                if (items.contains(Item.ALL) || items.contains(Item.UN_MODIFY))
                {
                    log.info("需要防篡改");
                    String sign = request.getHeader("sign");
                    if (StringUtils.isBlank(sign))
                    {
                        errors.add("检查防篡改失败");
                    }
                }
            }
            
            // 汇总返回错误
            if (!CollectionUtils.isEmpty(errors))
            {
                Signature signature = joinPoint.getSignature();
                if (signature instanceof MethodSignature)
                {
                    MethodSignature methodSignature = (MethodSignature)signature;
                    Method method = methodSignature.getMethod();
                    Class<?> methodReturnType = method.getReturnType();
                    // 返回匹配类型数据
                    if (StringUtils.equals(methodReturnType.getName(), String.class.getName()))
                    {
                        return JsonBeanUtils.beanToJson(JsonResult.error(StringUtils.join(errors.toArray(), ",")));
                    }
                    if (StringUtils.equals(methodReturnType.getName(), JsonResult.class.getName()))
                    {
                        return JsonResult.error(StringUtils.join(errors.toArray(), ","));
                    }
                    // 返回bytes[]/Object,不可返回JsonResult，否则接受不到数据
                    if (StringUtils.containsAny(methodReturnType.getName(), byte[].class.getName(), Object.class.getName()))
                    {
                        String result = JsonBeanUtils.beanToJson(JsonResult.error(StringUtils.join(errors.toArray(), ",")));
                        return result.getBytes(StandardCharsets.UTF_8);
                    }
                    // 返回void/StreamingResponseBody
                    if (StringUtils.containsAny(methodReturnType.getName(), void.class.getName(), StreamingResponseBody.class.getName()))
                    {
                        HttpServletResponse response = HttpServletUtils.getHttpServletResponse();
                        response.setHeader("Content-Disposition", "attachment;filename=result.txt");
                        JsonResult<?> result = JsonResult.error(StringUtils.join(errors.toArray(), ","));
                        try (ServletOutputStream out = response.getOutputStream())
                        {
                            out.write(JsonBeanUtils.beanToJson(result, true).getBytes(StandardCharsets.UTF_8));
                        }
                        return null;
                    }
                }
            }
        }
        log.info("------ around before ------");
        Object object = joinPoint.proceed();
        log.info("------ around after  ------");
        return object;
    }
}
